///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.circular
{
  import flash.geom.Rectangle;
  
  import ilog.gauges.gaugesClasses.GaugeBase;
  import ilog.gauges.gaugesClasses.GaugeElement;
  
  import mx.controls.Label;
  import mx.core.UIComponent;

  /**
   * The <code>CircularGauge</code> class is the top-level class of the ILOG Elixir
   * Circular Gauges Framework. It can be used to build custom circular gauges
   * in MXML or ActionScript.
   * 
   * Alternatively, ILOG Elixir provides predefined circular gauges in 
   * <code>ilog.gauges.controls</code> subpackages. 
   *
   * A circular gauge is defined by a set of logical scales and a set of visual 
   * elements, such as <code>CircularScaleRenderer</code>, <code>CircleRenderer</code>, 
   * <code>CircularTrackRenderer</code>, or <code>NeedleRenderer</code>.
   * 
   * The <code>CircularGauge</code> class sets the size and position of its visual 
   * elements according to padding values and to the <code>aspectRatio</code> property.
   * 
   * External assets can be managed using the <code>CircularGaugeAsset</code> class,   
   * which is a visual element.
   * 
   * <p>The <i>scales</i> array contains logical scales. For circular gauges, 
   * scales have to implement the <code>ICircularGauge</code> interface, for example,
   * <code>CircularLinearScale</code> or <code>CircularCategoryScale</code>.</p>
   * 
   * <p>The <i>elements</i> array contains the visual elements.</p>
   * 
   * @mxml
   *  
   * <p>The <code>&lt;ilog:CircularGauge&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:CircularGauge
   *   <strong>Properties</strong>
   *   aspectRatio="1.0"
   *   title=""
   *   titleGap="3"
   *   titlePlacement="bottom|top"
   *  /&gt;
   * </pre>
   *
   *  
   * @see ilog.gauges.gaugesClasses.circular.ICircularScale
   * @see ilog.gauges.gaugesClasses.circular.CircularGaugeElement
   * @includeExample examples/CircularGaugeExample.mxml -noswf 
   */  
  public class CircularGauge extends GaugeBase
  {
    /**
     * Constructor.
     */
    public function CircularGauge()
    {
      super();
    }

    /**
     *  @private
     */
    private var _labelField:Label;

    /**
     *  @private
     */
    private var _titleDirty:Boolean = false;
    
    /**
     *  @private
     */
    private var _aspectRatio:Number = 1;
    
    [Inspectable(category="Gauge")]    

    /**
     * This property controls the size of the visual elements.
     * A ratio of 2.0 means that the width of the elements is
     * twice their height.
     * If the property is set to <code>NaN</code>, the visual elements are not
     * sized and positioned by the gauge. It is useful for making fixed-size
     * circular gauges.
     * 
     * @default 1.0
     * 
     */
    public function get aspectRatio():Number
    {
      return _aspectRatio;
    }

    /**
     *  @private
     */
    public function set aspectRatio(value:Number):void
    {
      _aspectRatio = value;
      invalidateDisplayList();      
    }    
    
    /**
     * @private
     */
    public override function set title(value:String):void 
    {          
      super.title = value;
      _titleDirty = true;      
      invalidateProperties();      
    }

    /**
     * @private
     */
    protected override function commitProperties():void 
    {
      super.commitProperties();  
      if (_titleDirty) {
        _titleDirty = false;        
        if (getStyle("showTitle") == true) {
          _labelField.text = title;
          invalidateSize();
        }
      }
    }      

    /**
     * @private 
     */ 
    override public function styleChanged(styleProp:String):void
    {
      super.styleChanged(styleProp);
      if (styleProp == null || styleProp == "showTitle") {
        if (getStyle("showTitle") == true) {
          if (_labelField == null) {
            _labelField = new Label();          
          }
          // we might have a pending label to set
          _labelField.text = title;
        }
      }
      if (styleProp == null || styleProp == "titleStyleName") {
        if (_labelField != null) {
          _labelField.styleName = getStyle("titleStyleName");
        }
      }      
    }        

    /**
     * @private
     */
    override protected function measure():void 
    {
      super.measure();
      
      var width:Number = 0;
      var height:Number = 0;
        
      for each (var element:GaugeElement in elements)
      {
        width = Math.max(width, element.getExplicitOrMeasuredWidth());
        height = Math.max(height, element.getExplicitOrMeasuredHeight());
      }
      if (width == 0 || height == 0)
      {
        measuredWidth = 400;
        measuredHeight = 400;
      }
      else
      {
        var titleHeight:Number = 0;
        if (getStyle("showTitle") == true)
        {
          titleHeight = _labelField.getExplicitOrMeasuredHeight() + titleGap;
        }
          
        measuredMinWidth = measuredWidth = width + getStyle("paddingLeft") + getStyle("paddingRight");
        measuredMinHeight = measuredHeight = height + getStyle("paddingTop") + getStyle("paddingBottom") + titleHeight;
      }
    }

    /**
     * @private
     */
    private function layout():Rectangle
    {
      var xpos:Number = getStyle("paddingLeft");
      var drawableAreaY:Number = getStyle("paddingTop");
      var ypos:Number = drawableAreaY;

      var w:Number = unscaledWidth - xpos - getStyle("paddingRight");
      var drawableAreaH:Number = unscaledHeight - ypos - getStyle("paddingBottom");
      var h:Number = drawableAreaH;

      var aRatio:Number = w / h;

      if (aspectRatio > aRatio)
      {
        h = w / aspectRatio;
        ypos = (unscaledHeight - h) / 2;
      }
      else
      {
        w = h * aspectRatio;
        xpos = (unscaledWidth - w) / 2;
      }
    
      if (getStyle("showTitle") == true && _labelField.text != "")
      {
        _labelField.setStyle("textAlign", "center");
        var labelY:Number;
        if (! contains(_labelField))
          addChild(_labelField);
        var labelHeight:Number = _labelField.getExplicitOrMeasuredHeight() + titleGap;
        _labelField.setActualSize(unscaledWidth, _labelField.getExplicitOrMeasuredHeight());        
        
        if (drawableAreaH - h >= labelHeight + titleGap)
        {
          if (drawableAreaH - h >= 2 * (labelHeight + titleGap))
          {
            if (titlePlacement == "top")
            {
              _labelField.move(0, ypos - labelHeight - titleGap);
            } 
            else
            {
              _labelField.move(0, ypos + h + titleGap);  
            }
          }
          else
          {
            if (titlePlacement == "top")
            {
              _labelField.move(0, drawableAreaY);
              ypos = drawableAreaY + labelHeight + titleGap;
            } 
            else
            {
              labelY = drawableAreaY + drawableAreaH - labelHeight;
              _labelField.move(0, labelY);
              ypos = labelY - h - titleGap;
            }
          }
        }
        else
        {
          h -= labelHeight + titleGap - (drawableAreaH - h);
          if (titlePlacement == "top")
          {
            ypos = drawableAreaY + labelHeight + titleGap;
            _labelField.move(0, ypos - labelHeight - titleGap);
          } 
          else
          {
              labelY = drawableAreaY + drawableAreaH - labelHeight;
              _labelField.move(0, labelY);
              ypos = labelY - h - titleGap;
          }
        }
      }
      else
      {
        if (_labelField != null && contains(_labelField))
          removeChild(_labelField);
      } 
      return new Rectangle(xpos, ypos, w, h);  
    }

    /**
     * @private
     */
    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
      super.updateDisplayList(unscaledWidth, unscaledHeight);
      var drawingArea:Rectangle = layout();
      if (! isNaN(aspectRatio))
      {
        for each (var element:UIComponent in elements)
        {
          element.setActualSize(drawingArea.width, drawingArea.height);
          element.move(drawingArea.x, drawingArea.y);
        }
      }
    }
  }
}
